Reiniciar R

library(tidyverse)
library(openxlsx)
library(ggthemes)
library(fs)

Loops

Un loop es una estructura de código que nos permite aplicar iterativamente un mismo conjunto de comandos, variando el valor de una variable. Por ejemplo:

for(i in 1:10){
   print(i^2)
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

Esto se lee como : “Recorre cada uno de los valores (i) del vector numérico 1 a 10, y para cada uno de ellos imprimí el cuadrado (i^2)”.
Uno puede especificar la palabra que desee que tomé cada uno de los valores que debe tomar. En el ejemplo anterior fue i, pero bien podría ser la “Valores

for(Valores in 1:10){
   print(Valores^2)
  
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

Un loop puede iterar sobre cualquier tipo de vector, independientemente de lo que contenga.

Los loops son una estructura básica que existen en cualquier lenguaje de programación. En R no recomendamos abusar de ellos porque hacen que el código sea más lento.

Estructuras Condicionales

Las estructuras condiconales nos permiten ejecutar una porción de código en caso de que cumplan una condición lógica

if

Su funcionamiento es el siguiente:
if(condicion){codigo a ejecutar si se cumple la condición}

if( 2+2 == 4){
  print("Menos Mal")
}
[1] "Menos Mal"
if( 2+2 == 148.24){
  print("R, tenemos un problema")
}

ifelse

La función if_else() sirve para crear o modificar dicotómicamente un objeto/variable/vector a partir del cumplimiento de una o más condiciones lógicas.
Su funcionamiento es el siguiente:
if_else(condicion,función a aplicar si se cumple la condición,función a aplicar si no se cumple la condición)

if_else(2+2==4, true = "Joya",false = "Error")
[1] "Joya"

Funciones

La creación de funciones propias nos permite automatizar todas aquellas partes del código que se repiten mucho. Una vez diseñadas, funcionan igual que cualquier comando.

Por ejemplo, podemos definir la suma de dos elementos como

suma <- function(valor1, valor2) {
  valor1+valor2
}
suma(5,6)
[1] 11

Obviamente las funciones no son sólo para variables numéricas. Por ejemplo, podemos pegar dos strings con una flecha en el medio

funcion_prueba <- function(parametro1,parametro2) {
  paste(parametro1, parametro2, sep = " <--> ")
}
funcion_prueba(parametro1 = "A ver", parametro2 = "Que pasa")
[1] "A ver <--> Que pasa"

También podemos asignar un valor por default para los parametros en caso de que el usuario no defina su valor al utilizar la función.

Otra_funcion_prueba <- function(parametro1 ,parametro2 = "String default") {
  paste(parametro1, parametro2, sep = " <--> ")
  
}
Otra_funcion_prueba(parametro1 = "Valor 1 ")
[1] "Valor 1  <--> String default"

Las funciones que creamos nosotros permanecen en el ambiente de R temporariamente. Cuando removemos los objetos del ambiente, la función deja de existir. Por ende, debemos incorporarla en cada uno de los scripts en la cual la necesitemos. Una buena práctica, es incorporar nuestras funciones útiles al comienzo de cada script junto a la carga de las librerías.

Vale mencionar que lo que ocurre en una función, queda en la función excepto que explícitamente pidamos que devuelva el resultado, con el comando print().

Las funciones siempre devuelven el último objeto que se crea en ellas, o si explicitamente se utiliza el comando return()

Funciones anónimas

Son funciones a las cuales no les asignamos un nombre. Esto suele ser útil para hacer funciones pequeñas dentro de otras funciones o estructuras más complejas.

(function(x) x*2)(10)
[1] 20

PURRR1

MAP es la forma tidy de hacer loops. Además de ser más prolijo el código, es mucho más eficiente.

La función map toma un input, una función para aplicar, y alguna otra cosa (por ejemplo parametros que necesite la función)

  • map(.x, .f, …)
  • map(VECTOR_O_LIST_INPUT, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Usamos map2 cuando tenemos que pasar dos input, que se aplican sobre una función:

  • map2(.x, .y, .f, …)
  • map2(INPUT_UNO, INPUT_DOS, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Si tenemos más de dos…

  • pmap(.l, .f, …)
  • pmap(VECTOR_O_LIST_INPUT, FUNCTION_A_APLICAR, OTROS_OPCIONALES)

Por ejemplo. Si queremos utilizar la función prueba sobre los datos del dataframe ABC_123

ABC_123 <- data.frame(Letras = LETTERS[1:20],Num = 1:20)
funcion_prueba
function(parametro1,parametro2) {
  paste(parametro1, parametro2, sep = " <--> ")
}

Si el resultado que queremos es que junte cada fila, necesitamos pasarle dos parámetros: utilizamos map2(), Noten que la función se le pasa a map() sin paréntesis ni argumentos.

resultado <- map2(.x = ABC_123$Letras, .y = ABC_123$Num,.f = funcion_prueba)
resultado[1:3]
[[1]]
[1] "A <--> 1"

[[2]]
[1] "B <--> 2"

[[3]]
[1] "C <--> 3"

La salida de los map() es una lista, no un vector, por lo que si lo metemos dentro de un dataframe se vería así:

ABC_123 %>% 
  mutate(resultado= map2(.x = Letras,.y = Num,.f = funcion_prueba))

Al ponerlo dentro del dataframe desarma la lista y guarda cada elemento por separado. La magia de eso es que podemos guardar cualquier cosa en el dataframe no sólo valores, sino también listas, funciones, dataframes, etc.

Si queremos recuperar los valores originales en este caso podemos usar unlist()

# Recuperamos los tres primeros elementos de la lista resultados
resultado[1:3] %>% unlist()
[1] "A <--> 1" "B <--> 2" "C <--> 3"
# Recuperamos el elemento de la lista para cada par letra-numero (cada fila)
ABC_123 %>% 
  mutate(resultado= unlist(map2(.x = Letras,.y = Num,.f = funcion_prueba)))

Si lo que queríamos era que la función nos haga todas las combinaciones de letras y número, entonces lo que necesitamos es pasarle el segúndo parametro como algo fijo, poniendolo después de la función.

Notemos que estamos utilizando map() que toma un único input, en lugar de map2()

#Combinacion de todas las letras con todos los numeros
map(.x = ABC_123$Letras,.f = funcion_prueba,ABC_123$Num)[1:2]
[[1]]
 [1] "A <--> 1"  "A <--> 2"  "A <--> 3"  "A <--> 4"  "A <--> 5"  "A <--> 6"  "A <--> 7"  "A <--> 8"  "A <--> 9" 
[10] "A <--> 10" "A <--> 11" "A <--> 12" "A <--> 13" "A <--> 14" "A <--> 15" "A <--> 16" "A <--> 17" "A <--> 18"
[19] "A <--> 19" "A <--> 20"

[[2]]
 [1] "B <--> 1"  "B <--> 2"  "B <--> 3"  "B <--> 4"  "B <--> 5"  "B <--> 6"  "B <--> 7"  "B <--> 8"  "B <--> 9" 
[10] "B <--> 10" "B <--> 11" "B <--> 12" "B <--> 13" "B <--> 14" "B <--> 15" "B <--> 16" "B <--> 17" "B <--> 18"
[19] "B <--> 19" "B <--> 20"

En este caso, el map itera sobre cada elemento de letras, y para cada elemento i hace funcion_prueba(i,ABC$Num) y guarda el resultado en la lista

Si lo queremos meter en el dataframe:

# Asignar resultado al dataframe
ABC_123 %>% 
  mutate(resultado= map(Letras,funcion_prueba,Num))

Ahora cada fila tiene una lista de 20 elementos guardado en la columna resultado

Funciones implícitas/anonimas

No es necesario que definamos la función de antemano. Podemos usar funciones implícitas

# Calculamos el cuadrado de cada elemento
map_dbl(.x = c(1:10), .f = function(x) x^2) 
 [1]   1   4   9  16  25  36  49  64  81 100
# Calculamos el producto para los elementos x e y
map2_dbl(.x = c(1:10),.y = c(11:20),.f =  function(x,y) x*y)
 [1]  11  24  39  56  75  96 119 144 171 200

Funciones lambda

Incluso más conciso que las funciones implíictas son las funciones lambda donde definimos las variables como .x .y, etc. La flexibilidad de estas expresiones es limitada, pero puede ser útil en algunos casos.

# Calculamos el cuadrado de cada elemento
map_dbl(.x = c(1:10),.f = ~.x^2)
 [1]   1   4   9  16  25  36  49  64  81 100
# Calculamos el producto para los elementos x e y
map2_dbl(.x = c(1:10),.y = c(11:20),.f = ~.x*.y)
 [1]  11  24  39  56  75  96 119 144 171 200

Ejemplo: Iterando en la EPH

Lo primero que necesitamos es definir un vector o lista sobre el que iterar.

Por ejemplo, podemos armar un vector con los path a las bases individuales, con el comando fs::dir_ls. Se le especifica el path donde buscar los archivos y en este caso una expresion regular regexp para que devuelva aquellos archivos que coinciden con la misma.

# Buscamos en el path aquellos aquellos archivos que matchean a la expresion regular
bases_individuales_path <- dir_ls(path = '../Fuentes/', regexp= 'individual')
bases_individuales_path
../Fuentes/usu_individual_t117.txt ../Fuentes/usu_individual_t216.txt ../Fuentes/usu_individual_t316.txt 
../Fuentes/usu_individual_t416.txt 

Luego, como en la función que usamos para leer las bases definimos muchos parametros, nos podemos armar una función wrapper que sólo necesite un parámetro, y que simplifique la escritura del map.

# Leer la base de EPH tomando como argumento el file_path
leer_base_eph <- function(path) {
  # Lectura de archivo
  read.table(path,sep=";", dec=",", header = TRUE, fill = TRUE) %>%
    # Seleccion de variables relevantes
    select(ANO4,TRIMESTRE,REGION,P21,CH04, CH06)
}
# Leer a un dataframe las tablas especificadas en el vector de bases individuales
bases_df <- tibble(bases_individuales_path) %>%
  mutate(base = map(.x = bases_individuales_path, .f = leer_base_eph))
bases_df

El resultado es un dataframe donde la columna base tiene en cada fila otro dataframe con la base de la EPH de ese período. Esto es lo que llamamos un nested DF o dataframe anidado.

Si queremos abrir (“desanidar”) estas dataframes anidados usamos el comando unnest()

# Desanidamos el dataframe
bases_df <- bases_df %>% unnest()
bases_df

¿Qué pasa si los DF que tenemos nesteados no tienen la misma cantidad de columnas?

Esto mismo lo podemos usar para fragmentar el dataset por alguna variable, con la funcion group_by()

bases_df %>% 
  # Agrupar por region
  group_by(REGION) %>% 
  # Anidar
  nest()

Así, para cada región tenemos un DF.

¿ De qué sirve todo esto?

No todo en la vida es un Dataframe. Hay estucturas de datos que no se pueden normalizar a filas y columnas. En esos casos recurríamos tradicionalmente a los loops. Con MAP podemos tener los elementos agrupados en un sólo objeto y aún conservar sus formas diferentes.

Ejemplo. Regresión lineal

Si bien no nos vamos a meter en el detalle del modelo lineal en este encuentro, es útil usarlo como ejemplo de lo que podemos hacer con MAP.

Planteamos el modelo \[ P21 = \beta_0 + \beta_1*CH04 + \beta_2*CH06 \]

Es un modelo que explica el ingreso (P21) en funcion del sexo (CH04) y edad (CH06)

# Calculamos una regresion lineal sobre el dataset que tiene todas las observaciones de EPH
lmfit <- lm(P21~factor(CH04)+CH06,data = bases_df)
# Resumen del modelo
summary(lmfit)

Call:
lm(formula = P21 ~ factor(CH04) + CH06, data = bases_df)

Residuals:
   Min     1Q Median     3Q    Max 
 -8449  -3631  -1938    705 393616 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    2599.1101    29.0732   89.40   <2e-16 ***
factor(CH04)2 -2250.9637    28.3026  -79.53   <2e-16 ***
CH06             57.3490     0.6347   90.35   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6858 on 236187 degrees of freedom
Multiple R-squared:  0.05449,   Adjusted R-squared:  0.05448 
F-statistic:  6805 on 2 and 236187 DF,  p-value: < 2.2e-16

(al final de la clase podemos charlar sobre los resultados)

De forma Tidy, la librería broom nos da los resultados sobre los coeficientes en un DF.

broom::tidy(lmfit)

Si lo queremos hacer por region

Loopeando

resultados <- tibble()
for (region in unique(bases_df$REGION)) {
  # Creamos un dataset por region
  data <- bases_df %>% 
    filter(REGION==region)
  # Calcular el modelo lineal
  lmfit <- lm(P21~factor(CH04)+CH06,data = data)
  # Guardar los resultados de los coeficientes 
  lmtidy <- broom::tidy(lmfit)
  # Asignamos la variable region
  lmtidy$region <- region
  # Unimos los dataframes
  resultados <- bind_rows(resultados,lmtidy)
}
resultados

Usando MAP

Primero me armo una funcion que me simplifica el codigo. La funcion nos devuelve la informacion de los coeficientes del modelo lineal calculado sobre el dataframe porcion

fun<-function(porcion,grupo) {  broom::tidy(lm(P21~factor(CH04)+CH06,data = porcion))}
bases_df_lm <- bases_df %>% 
  # Agrupamos por region
  group_by(REGION) %>%
  # Anidamos
  nest() %>% 
  # Creamos una columna que tenga el dataframe como resultado de la funcion
  mutate(lm = map(data,fun))
bases_df_lm
# Desanidamos por la variable lm
bases_df_lm %>% 
  unnest(lm)

O incluso más facil, utilizando group_modify (que es un atajo que solo acepta DF)

bases_df %>% 
  group_by(REGION) %>% 
  group_modify(fun)

Pero MAP sirve para operar con cualquier objeto de R.

Por ejemplo podemos guardar el objeto S3:lm que es la regresion lineal entrenada. Ese objeto no es ni un vector, ni una lista, ni un DF. No es una estructura de datos, sino que es algo distinto, con propiedades como predict() para predecir, el summary() que vimos, etc.

# La funcion calcula el modelo lineal sobre cada porcion de datos
fun<-function(porcion,grupo) {  lm(P21~factor(CH04)+CH06,data = porcion)}
bases_df %>%
  # Agrupar por region
  group_by(REGION) %>%
  # Anidar
  nest() %>%
  # Calcular el modelo lineal
  mutate(lm = map(data,fun))

Walk

Las funciones Walk Tienen la misma forma que los map, pero se usan cuando lo que queremos iterar no genera una salida, sino que nos interesan los efectos secundarios que generan.

map2(.x = ABC_123$Letras,.y = ABC_123$Num,.f = funcion_prueba)[1:3]
[[1]]
[1] "A <--> 1"

[[2]]
[1] "B <--> 2"

[[3]]
[1] "C <--> 3"
walk2(.x = ABC_123$Letras,.y = ABC_123$Num,.f = funcion_prueba)

Notemos que walk2 no devolvió un resultado

# funcion para imprimir
imprimir_salida <- function(x,y){
  print(funcion_prueba(x,y))
}
# Map
map2(ABC_123$Letras,ABC_123$Num,imprimir_salida)
[1] "A <--> 1"
[1] "B <--> 2"
[1] "C <--> 3"
[1] "D <--> 4"
[1] "E <--> 5"
[1] "F <--> 6"
[1] "G <--> 7"
[1] "H <--> 8"
[1] "I <--> 9"
[1] "J <--> 10"
[1] "K <--> 11"
[1] "L <--> 12"
[1] "M <--> 13"
[1] "N <--> 14"
[1] "O <--> 15"
[1] "P <--> 16"
[1] "Q <--> 17"
[1] "R <--> 18"
[1] "S <--> 19"
[1] "T <--> 20"
[[1]]
[1] "A <--> 1"

[[2]]
[1] "B <--> 2"

[[3]]
[1] "C <--> 3"

[[4]]
[1] "D <--> 4"

[[5]]
[1] "E <--> 5"

[[6]]
[1] "F <--> 6"

[[7]]
[1] "G <--> 7"

[[8]]
[1] "H <--> 8"

[[9]]
[1] "I <--> 9"

[[10]]
[1] "J <--> 10"

[[11]]
[1] "K <--> 11"

[[12]]
[1] "L <--> 12"

[[13]]
[1] "M <--> 13"

[[14]]
[1] "N <--> 14"

[[15]]
[1] "O <--> 15"

[[16]]
[1] "P <--> 16"

[[17]]
[1] "Q <--> 17"

[[18]]
[1] "R <--> 18"

[[19]]
[1] "S <--> 19"

[[20]]
[1] "T <--> 20"
# Walk
walk2(ABC_123$Letras,ABC_123$Num,imprimir_salida)
[1] "A <--> 1"
[1] "B <--> 2"
[1] "C <--> 3"
[1] "D <--> 4"
[1] "E <--> 5"
[1] "F <--> 6"
[1] "G <--> 7"
[1] "H <--> 8"
[1] "I <--> 9"
[1] "J <--> 10"
[1] "K <--> 11"
[1] "L <--> 12"
[1] "M <--> 13"
[1] "N <--> 14"
[1] "O <--> 15"
[1] "P <--> 16"
[1] "Q <--> 17"
[1] "R <--> 18"
[1] "S <--> 19"
[1] "T <--> 20"

Eso que vemos es el efecto secundario dentro de la función (imprimir)

Discusión.

¿Cuándo usar estas herramientas?

A lo largo del curso vimos diferentes técnicas para manipulación de datos. En particular, la librería dplyr nos permitía fácilmente modificar y crear nuevas variables, agrupando. ¿Cuando usamos dplyr y cuando usamos purrr?

  • Si trabajamos sobre un DF simple, sin variables anidadas (lo que conocíamos hasta hoy) podemos usar dplyr
  • Si queremos trabajar con DF anidados, con cosas que no son DF, o si el resultado de la operación que vamos a realizar a nivel archivo es algo distinto a un valor único, nos conviene usar map y purrr

  • Las funciones walk son útiles por ejemplo para escribir archivos en disco de forma iterativa. Algo que no genera una salida

Ejercicios para practicar

  • Crear una función llamada HolaMundo que imprima el texto “Hola mundo”
  • Crear una función que devuelva la sumatoria de los números enteros comprendidos entre 1 y un parámetro x a definir.

  • Crear una función que calcule la frecuencia expandida por un PONDERA
  • Utilizar dicha función para calcular la frecuencia poblaciónal por Sexo y Región

  • Modificar la función anterior para que devuelva un vector con la frecuencia muestra y la frecuencia poblacional
  • Utilizar la función modificada para calcular la frecuencias frecuencias muestrales y poblacionales por Sexo y Región

LS0tCnRpdGxlOiAiQ2xhc2UgMSAtIFByb2dyYW1hY2lvbiBGdW5jaW9uYWwiCmF1dGhvcjogIkRpZWdvIEtvemxvd3NraSB5IEp1YW4gQmFycmlvbGEiCmRhdGU6ICIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KZGl2Lm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDE2MDBweDsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87Cn0KPC9zdHlsZT4KCj5SZWluaWNpYXIgUgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkoZnMpCmBgYAoKIyBMb29wcwoKVW4gX19sb29wX18gZXMgdW5hIGVzdHJ1Y3R1cmEgZGUgY8OzZGlnbyBxdWUgbm9zIHBlcm1pdGUgYXBsaWNhciBpdGVyYXRpdmFtZW50ZSB1biBtaXNtbyBjb25qdW50byBkZSBjb21hbmRvcywgdmFyaWFuZG8gZWwgdmFsb3IgZGUgdW5hIHZhcmlhYmxlLiBQb3IgZWplbXBsbzoKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmZvcihpIGluIDE6MTApewogICBwcmludChpXjIpCn0KYGBgCgpFc3RvIHNlIGxlZSBjb21vIDogIlJlY29ycmUgY2FkYSB1bm8gZGUgbG9zIHZhbG9yZXMgKGkpIGRlbCB2ZWN0b3IgbnVtw6lyaWNvIDEgYSAxMCwgeSBwYXJhIGNhZGEgdW5vIGRlIGVsbG9zIGltcHJpbcOtIGVsIGN1YWRyYWRvIChpXjIpIi4gICAgICAgICAgICAgICAgICAKVW5vIHB1ZWRlIGVzcGVjaWZpY2FyIGxhIHBhbGFicmEgcXVlIGRlc2VlIHF1ZSB0b23DqSBjYWRhIHVubyBkZSBsb3MgdmFsb3JlcyBxdWUgZGViZSB0b21hci4gRW4gZWwgZWplbXBsbyBhbnRlcmlvciBmdWUgKippKiosIHBlcm8gYmllbiBwb2Ryw61hIHNlciBsYSAiKipWYWxvcmVzKioiICAKCmBgYHtyfQpmb3IoVmFsb3JlcyBpbiAxOjEwKXsKICAgcHJpbnQoVmFsb3Jlc14yKQogIAp9CmBgYAoKVW4gbG9vcCBwdWVkZSBpdGVyYXIgc29icmUgY3VhbHF1aWVyIHRpcG8gZGUgdmVjdG9yLCBpbmRlcGVuZGllbnRlbWVudGUgZGUgbG8gcXVlIGNvbnRlbmdhLgoKPiBMb3MgbG9vcHMgc29uIHVuYSBlc3RydWN0dXJhIGLDoXNpY2EgcXVlIGV4aXN0ZW4gZW4gY3VhbHF1aWVyIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24uIEVuIFIgbm8gcmVjb21lbmRhbW9zIGFidXNhciBkZSBlbGxvcyBwb3JxdWUgaGFjZW4gcXVlIGVsIGPDs2RpZ28gc2VhIG3DoXMgbGVudG8uCgoKIyBFc3RydWN0dXJhcyBDb25kaWNpb25hbGVzCgpMYXMgX19lc3RydWN0dXJhcyBjb25kaWNvbmFsZXNfXyBub3MgcGVybWl0ZW4gZWplY3V0YXIgdW5hIHBvcmNpw7NuIGRlIGPDs2RpZ28gZW4gY2FzbyBkZSBxdWUgY3VtcGxhbiB1bmEgY29uZGljacOzbiBsw7NnaWNhCgojIyBpZgpTdSBmdW5jaW9uYW1pZW50byBlcyBlbCBzaWd1aWVudGU6ICAgICAKYGBgaWYoY29uZGljaW9uKXtjb2RpZ28gYSBlamVjdXRhciBzaSBzZSBjdW1wbGUgbGEgY29uZGljacOzbn1gYGAKCgpgYGB7cix3YXJuaW5nPUZBTFNFfQppZiggMisyID09IDQpewogIHByaW50KCJNZW5vcyBNYWwiKQp9CgpgYGAKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmlmKCAyKzIgPT0gMTQ4LjI0KXsKICBwcmludCgiUiwgdGVuZW1vcyB1biBwcm9ibGVtYSIpCn0KCmBgYAoKCiMjIGlmZWxzZQpMYSBmdW5jacOzbiBgYGBpZl9lbHNlKClgYGAgc2lydmUgcGFyYSBjcmVhciBvIG1vZGlmaWNhciBkaWNvdMOzbWljYW1lbnRlIHVuIG9iamV0by92YXJpYWJsZS92ZWN0b3IgYSBwYXJ0aXIgZGVsIGN1bXBsaW1pZW50byBkZSB1bmEgbyBtw6FzIGNvbmRpY2lvbmVzIGzDs2dpY2FzLiAgClN1IGZ1bmNpb25hbWllbnRvIGVzIGVsIHNpZ3VpZW50ZTogICAgICAKYGBgaWZfZWxzZShjb25kaWNpb24sZnVuY2nDs24gYSBhcGxpY2FyIHNpIHNlIGN1bXBsZSBsYSBjb25kaWNpw7NuLGZ1bmNpw7NuIGEgYXBsaWNhciBzaSBubyBzZSBjdW1wbGUgbGEgY29uZGljacOzbilgYGAKCmBgYHtyfQppZl9lbHNlKDIrMj09NCwgdHJ1ZSA9ICJKb3lhIixmYWxzZSA9ICJFcnJvciIpCmBgYAoKIyBGdW5jaW9uZXMKCkxhIGNyZWFjacOzbiBkZSBfX2Z1bmNpb25lc19fIHByb3BpYXMgbm9zIHBlcm1pdGUgYXV0b21hdGl6YXIgdG9kYXMgYXF1ZWxsYXMgcGFydGVzIGRlbCBjw7NkaWdvIHF1ZSBzZSByZXBpdGVuIG11Y2hvLiBVbmEgdmV6IGRpc2XDsWFkYXMsIGZ1bmNpb25hbiBpZ3VhbCBxdWUgY3VhbHF1aWVyIGNvbWFuZG8uIAoKUG9yIGVqZW1wbG8sIHBvZGVtb3MgZGVmaW5pciBsYSBzdW1hIGRlIGRvcyBlbGVtZW50b3MgY29tbwoKCmBgYHtyfQpzdW1hIDwtIGZ1bmN0aW9uKHZhbG9yMSwgdmFsb3IyKSB7CiAgdmFsb3IxK3ZhbG9yMgp9CgpzdW1hKDUsNikKYGBgCgoKT2J2aWFtZW50ZSBsYXMgZnVuY2lvbmVzIG5vIHNvbiBzw7NsbyBwYXJhIHZhcmlhYmxlcyBudW3DqXJpY2FzLiBQb3IgZWplbXBsbywgcG9kZW1vcyBwZWdhciBkb3Mgc3RyaW5ncyBjb24gdW5hIGZsZWNoYSBlbiBlbCBtZWRpbwoKYGBge3J9CmZ1bmNpb25fcHJ1ZWJhIDwtIGZ1bmN0aW9uKHBhcmFtZXRybzEscGFyYW1ldHJvMikgewogIHBhc3RlKHBhcmFtZXRybzEsIHBhcmFtZXRybzIsIHNlcCA9ICIgPC0tPiAiKQp9CgpmdW5jaW9uX3BydWViYShwYXJhbWV0cm8xID0gIkEgdmVyIiwgcGFyYW1ldHJvMiA9ICJRdWUgcGFzYSIpCgpgYGAKClRhbWJpw6luIHBvZGVtb3MgYXNpZ25hciB1biB2YWxvciBwb3IgZGVmYXVsdCBwYXJhIGxvcyBwYXJhbWV0cm9zIGVuIGNhc28gZGUgcXVlIGVsIHVzdWFyaW8gbm8gZGVmaW5hIHN1IHZhbG9yIGFsIHV0aWxpemFyIGxhIGZ1bmNpw7NuLgoKYGBge3J9Ck90cmFfZnVuY2lvbl9wcnVlYmEgPC0gZnVuY3Rpb24ocGFyYW1ldHJvMSAscGFyYW1ldHJvMiA9ICJTdHJpbmcgZGVmYXVsdCIpIHsKICBwYXN0ZShwYXJhbWV0cm8xLCBwYXJhbWV0cm8yLCBzZXAgPSAiIDwtLT4gIikKICAKfQpPdHJhX2Z1bmNpb25fcHJ1ZWJhKHBhcmFtZXRybzEgPSAiVmFsb3IgMSAiKQoKYGBgCgpMYXMgZnVuY2lvbmVzIHF1ZSBjcmVhbW9zIG5vc290cm9zIHBlcm1hbmVjZW4gZW4gZWwgYW1iaWVudGUgZGUgUiB0ZW1wb3JhcmlhbWVudGUuIEN1YW5kbyByZW1vdmVtb3MgbG9zIG9iamV0b3MgZGVsIGFtYmllbnRlLCBsYSBmdW5jacOzbiBkZWphIGRlIGV4aXN0aXIuIFBvciBlbmRlLCBkZWJlbW9zIGluY29ycG9yYXJsYSBlbiBjYWRhIHVubyBkZSBsb3Mgc2NyaXB0cyBlbiBsYSBjdWFsIGxhIG5lY2VzaXRlbW9zLiBVbmEgYnVlbmEgcHLDoWN0aWNhLCBlcyBpbmNvcnBvcmFyIG51ZXN0cmFzIGZ1bmNpb25lcyDDunRpbGVzIGFsIGNvbWllbnpvIGRlIGNhZGEgc2NyaXB0IGp1bnRvIGEgbGEgY2FyZ2EgZGUgbGFzIGxpYnJlcsOtYXMuICAgICAgICAgICAgICAgICAgICAgICAgIAoKVmFsZSBtZW5jaW9uYXIgcXVlIF9fbG8gcXVlIG9jdXJyZSBlbiB1bmEgZnVuY2nDs24sIHF1ZWRhIGVuIGxhIGZ1bmNpw7NuX18gZXhjZXB0byBxdWUgZXhwbMOtY2l0YW1lbnRlIHBpZGFtb3MgcXVlIGRldnVlbHZhIGVsIHJlc3VsdGFkbywgY29uIGVsIGNvbWFuZG8gYHByaW50KClgLiAKCkxhcyBmdW5jaW9uZXMgc2llbXByZSBkZXZ1ZWx2ZW4gZWwgw7psdGltbyBvYmpldG8gcXVlIHNlIGNyZWEgZW4gZWxsYXMsIG8gc2kgZXhwbGljaXRhbWVudGUgc2UgdXRpbGl6YSBlbCBjb21hbmRvIGByZXR1cm4oKWAKCioqRnVuY2lvbmVzIGFuw7NuaW1hcyoqCgpTb24gZnVuY2lvbmVzIGEgbGFzIGN1YWxlcyBubyBsZXMgYXNpZ25hbW9zIHVuIG5vbWJyZS4gRXN0byBzdWVsZSBzZXIgw7p0aWwgcGFyYSBoYWNlciBmdW5jaW9uZXMgcGVxdWXDsWFzIGRlbnRybyBkZSBvdHJhcyBmdW5jaW9uZXMgbyBlc3RydWN0dXJhcyBtw6FzIGNvbXBsZWphcy4KCmBgYHtyfQooZnVuY3Rpb24oeCkgeCoyKSgxMCkKYGBgCgoKIyBQVVJSUl5bYmFzYWRvIGVuIGh0dHBzOi8vamVubnliYy5naXRodWIuaW8vcHVycnItdHV0b3JpYWwvbHMwM19tYXAtZnVuY3Rpb24tc3ludGF4Lmh0bWxdCgpNQVAgZXMgbGEgZm9ybWEgX3RpZHlfIGRlIGhhY2VyIGxvb3BzLiBBZGVtw6FzIGRlIHNlciBtw6FzIHByb2xpam8gZWwgY8OzZGlnbywgZXMgbXVjaG8gbcOhcyBlZmljaWVudGUuCgpMYSBmdW5jacOzbiBfX21hcF9fIHRvbWEgdW4gaW5wdXQsIHVuYSBmdW5jacOzbiBwYXJhIGFwbGljYXIsIHkgYWxndW5hIG90cmEgY29zYSAocG9yIGVqZW1wbG8gcGFyYW1ldHJvcyBxdWUgbmVjZXNpdGUgbGEgZnVuY2nDs24pCgotIG1hcCgueCwgLmYsIC4uLikKLSBtYXAoVkVDVE9SX09fTElTVF9JTlBVVCwgRlVOQ1RJT05fQV9BUExJQ0FSLCBPVFJPU19PUENJT05BTEVTKQoKClVzYW1vcyBfX21hcDJfXyBjdWFuZG8gdGVuZW1vcyBxdWUgcGFzYXIgZG9zIGlucHV0LCBxdWUgc2UgYXBsaWNhbiBzb2JyZSB1bmEgZnVuY2nDs246CgotIG1hcDIoLngsIC55LCAuZiwgLi4uKQotIG1hcDIoSU5QVVRfVU5PLCBJTlBVVF9ET1MsIEZVTkNUSU9OX0FfQVBMSUNBUiwgT1RST1NfT1BDSU9OQUxFUykKClNpIHRlbmVtb3MgbcOhcyBkZSBkb3MuLi4KCi0gcG1hcCgubCwgLmYsIC4uLikKLSBwbWFwKFZFQ1RPUl9PX0xJU1RfSU5QVVQsIEZVTkNUSU9OX0FfQVBMSUNBUiwgT1RST1NfT1BDSU9OQUxFUykKCgpQb3IgZWplbXBsby4gU2kgcXVlcmVtb3MgdXRpbGl6YXIgbGEgZnVuY2nDs24gcHJ1ZWJhIHNvYnJlIGxvcyBkYXRvcyBkZWwgZGF0YWZyYW1lIEFCQ18xMjMKCgpgYGB7cn0KQUJDXzEyMyA8LSBkYXRhLmZyYW1lKExldHJhcyA9IExFVFRFUlNbMToyMF0sTnVtID0gMToyMCkKZnVuY2lvbl9wcnVlYmEKYGBgCgpTaSBlbCByZXN1bHRhZG8gcXVlIHF1ZXJlbW9zIGVzIHF1ZSBqdW50ZSBjYWRhIGZpbGEsIG5lY2VzaXRhbW9zIHBhc2FybGUgZG9zIHBhcsOhbWV0cm9zOiB1dGlsaXphbW9zIGBtYXAyKClgLCBOb3RlbiBxdWUgbGEgZnVuY2nDs24gc2UgbGUgcGFzYSBhIGBtYXAoKWAgc2luIHBhcsOpbnRlc2lzIG5pIGFyZ3VtZW50b3MuCgoKYGBge3J9CnJlc3VsdGFkbyA8LSBtYXAyKC54ID0gQUJDXzEyMyRMZXRyYXMsIC55ID0gQUJDXzEyMyROdW0sLmYgPSBmdW5jaW9uX3BydWViYSkKcmVzdWx0YWRvWzE6M10KCmBgYAoKTGEgc2FsaWRhIGRlIGxvcyBgbWFwKClgIGVzIHVuYSBfX2xpc3RhX18sIG5vIHVuIHZlY3RvciwgcG9yIGxvIHF1ZSBzaSBsbyBtZXRlbW9zIGRlbnRybyBkZSB1biBkYXRhZnJhbWUgc2UgdmVyw61hIGFzw606CgpgYGB7cn0KQUJDXzEyMyAlPiUgCiAgbXV0YXRlKHJlc3VsdGFkbz0gbWFwMigueCA9IExldHJhcywueSA9IE51bSwuZiA9IGZ1bmNpb25fcHJ1ZWJhKSkKCmBgYAoKQWwgcG9uZXJsbyBkZW50cm8gZGVsIGRhdGFmcmFtZSBkZXNhcm1hIGxhIGxpc3RhIHkgZ3VhcmRhIGNhZGEgZWxlbWVudG8gcG9yIHNlcGFyYWRvLgpMYSBtYWdpYSBkZSBlc28gZXMgcXVlIHBvZGVtb3MgX19ndWFyZGFyIGN1YWxxdWllciBjb3NhIGVuIGVsIGRhdGFmcmFtZV9fIG5vIHPDs2xvIHZhbG9yZXMsIHNpbm8gdGFtYmnDqW4gbGlzdGFzLCBmdW5jaW9uZXMsIGRhdGFmcmFtZXMsIGV0Yy4KClNpIHF1ZXJlbW9zIHJlY3VwZXJhciBsb3MgdmFsb3JlcyBvcmlnaW5hbGVzIGVuIGVzdGUgY2FzbyBwb2RlbW9zIHVzYXIgYHVubGlzdCgpYAoKYGBge3J9CiMgUmVjdXBlcmFtb3MgbG9zIHRyZXMgcHJpbWVyb3MgZWxlbWVudG9zIGRlIGxhIGxpc3RhIHJlc3VsdGFkb3MKcmVzdWx0YWRvWzE6M10gJT4lIHVubGlzdCgpCgojIFJlY3VwZXJhbW9zIGVsIGVsZW1lbnRvIGRlIGxhIGxpc3RhIHBhcmEgY2FkYSBwYXIgbGV0cmEtbnVtZXJvIChjYWRhIGZpbGEpCkFCQ18xMjMgJT4lIAogIG11dGF0ZShyZXN1bHRhZG89IHVubGlzdChtYXAyKC54ID0gTGV0cmFzLC55ID0gTnVtLC5mID0gZnVuY2lvbl9wcnVlYmEpKSkKYGBgCgpTaSBsbyBxdWUgcXVlcsOtYW1vcyBlcmEgcXVlIGxhIGZ1bmNpw7NuIG5vcyBoYWdhIHRvZGFzIGxhcyBjb21iaW5hY2lvbmVzIGRlIGxldHJhcyB5IG7Dum1lcm8sIGVudG9uY2VzIGxvIHF1ZSBuZWNlc2l0YW1vcyBlcyBwYXNhcmxlIGVsIHNlZ8O6bmRvIHBhcmFtZXRybyBjb21vIGFsZ28gX2Zpam9fLCBwb25pZW5kb2xvIGRlc3B1w6lzIGRlIGxhIGZ1bmNpw7NuLiAKCk5vdGVtb3MgcXVlIGVzdGFtb3MgdXRpbGl6YW5kbyBgbWFwKClgIHF1ZSB0b21hIHVuIMO6bmljbyBpbnB1dCwgZW4gbHVnYXIgZGUgYG1hcDIoKWAKCmBgYHtyfQojQ29tYmluYWNpb24gZGUgdG9kYXMgbGFzIGxldHJhcyBjb24gdG9kb3MgbG9zIG51bWVyb3MKbWFwKC54ID0gQUJDXzEyMyRMZXRyYXMsLmYgPSBmdW5jaW9uX3BydWViYSxBQkNfMTIzJE51bSlbMToyXQpgYGAKCkVuIGVzdGUgY2FzbywgZWwgbWFwIGl0ZXJhIHNvYnJlIGNhZGEgZWxlbWVudG8gZGUgYGxldHJhc2AsIHkgcGFyYSBjYWRhIGVsZW1lbnRvIF9pXyBoYWNlIApgZnVuY2lvbl9wcnVlYmEoaSxBQkMkTnVtKWAgeSBndWFyZGEgZWwgcmVzdWx0YWRvIGVuIGxhIGxpc3RhCgpTaSBsbyBxdWVyZW1vcyBtZXRlciBlbiBlbCBkYXRhZnJhbWU6CgpgYGB7cn0KIyBBc2lnbmFyIHJlc3VsdGFkbyBhbCBkYXRhZnJhbWUKQUJDXzEyMyAlPiUgCiAgbXV0YXRlKHJlc3VsdGFkbz0gbWFwKExldHJhcyxmdW5jaW9uX3BydWViYSxOdW0pKQpgYGAKCkFob3JhIGNhZGEgZmlsYSB0aWVuZSB1bmEgbGlzdGEgZGUgMjAgZWxlbWVudG9zIGd1YXJkYWRvIGVuIGxhIGNvbHVtbmEgKipyZXN1bHRhZG8qKgoKIyMgIEZ1bmNpb25lcyBpbXBsw61jaXRhcy9hbm9uaW1hcwoKTm8gZXMgbmVjZXNhcmlvIHF1ZSBkZWZpbmFtb3MgbGEgZnVuY2nDs24gZGUgYW50ZW1hbm8uIFBvZGVtb3MgdXNhciBfZnVuY2lvbmVzIGltcGzDrWNpdGFzXwoKYGBge3J9CiMgQ2FsY3VsYW1vcyBlbCBjdWFkcmFkbyBkZSBjYWRhIGVsZW1lbnRvCm1hcF9kYmwoLnggPSBjKDE6MTApLCAuZiA9IGZ1bmN0aW9uKHgpIHheMikgCiMgQ2FsY3VsYW1vcyBlbCBwcm9kdWN0byBwYXJhIGxvcyBlbGVtZW50b3MgeCBlIHkKbWFwMl9kYmwoLnggPSBjKDE6MTApLC55ID0gYygxMToyMCksLmYgPSAgZnVuY3Rpb24oeCx5KSB4KnkpCmBgYAoKIyMgRnVuY2lvbmVzIGxhbWJkYQoKSW5jbHVzbyBtw6FzIGNvbmNpc28gcXVlIGxhcyBmdW5jaW9uZXMgaW1wbMOtaWN0YXMgc29uIGxhcyBfX2Z1bmNpb25lcyBsYW1iZGFfXyBkb25kZSBkZWZpbmltb3MgbGFzIHZhcmlhYmxlcyBjb21vIF8ueF8gXy55XywgZXRjLiBMYSBmbGV4aWJpbGlkYWQgZGUgZXN0YXMgZXhwcmVzaW9uZXMgZXMgbGltaXRhZGEsIHBlcm8gcHVlZGUgc2VyIMO6dGlsIGVuIGFsZ3Vub3MgY2Fzb3MuIAoKYGBge3J9CiMgQ2FsY3VsYW1vcyBlbCBjdWFkcmFkbyBkZSBjYWRhIGVsZW1lbnRvCm1hcF9kYmwoLnggPSBjKDE6MTApLC5mID0gfi54XjIpCiMgQ2FsY3VsYW1vcyBlbCBwcm9kdWN0byBwYXJhIGxvcyBlbGVtZW50b3MgeCBlIHkKbWFwMl9kYmwoLnggPSBjKDE6MTApLC55ID0gYygxMToyMCksLmYgPSB+LngqLnkpCmBgYAoKCiMjIEVqZW1wbG86IEl0ZXJhbmRvIGVuIGxhIEVQSAoKTG8gcHJpbWVybyBxdWUgbmVjZXNpdGFtb3MgZXMgZGVmaW5pciB1biB2ZWN0b3IgbyBsaXN0YSBzb2JyZSBlbCBxdWUgaXRlcmFyLiAKClBvciBlamVtcGxvLCBwb2RlbW9zIGFybWFyIHVuIHZlY3RvciBjb24gbG9zIHBhdGggYSBsYXMgYmFzZXMgaW5kaXZpZHVhbGVzLCBjb24gZWwgY29tYW5kbyBgZnM6OmRpcl9sc2AuIFNlIGxlIGVzcGVjaWZpY2EgZWwgX19wYXRoX18gZG9uZGUgYnVzY2FyIGxvcyBhcmNoaXZvcyB5IGVuIGVzdGUgY2FzbyB1bmEgZXhwcmVzaW9uIHJlZ3VsYXIgX19yZWdleHBfXyBwYXJhIHF1ZSBkZXZ1ZWx2YSBhcXVlbGxvcyBhcmNoaXZvcyBxdWUgY29pbmNpZGVuIGNvbiBsYSBtaXNtYS4KCmBgYHtyfQojIEJ1c2NhbW9zIGVuIGVsIHBhdGggYXF1ZWxsb3MgYXF1ZWxsb3MgYXJjaGl2b3MgcXVlIG1hdGNoZWFuIGEgbGEgZXhwcmVzaW9uIHJlZ3VsYXIKYmFzZXNfaW5kaXZpZHVhbGVzX3BhdGggPC0gZGlyX2xzKHBhdGggPSAnLi4vRnVlbnRlcy8nLCByZWdleHA9ICdpbmRpdmlkdWFsJykKYmFzZXNfaW5kaXZpZHVhbGVzX3BhdGgKYGBgCgpMdWVnbywgY29tbyBlbiBsYSBmdW5jacOzbiBxdWUgdXNhbW9zIHBhcmEgbGVlciBsYXMgYmFzZXMgZGVmaW5pbW9zIG11Y2hvcyBwYXJhbWV0cm9zLCBub3MgcG9kZW1vcyBhcm1hciB1bmEgZnVuY2nDs24gX3dyYXBwZXJfIHF1ZSBzw7NsbyBuZWNlc2l0ZSB1biBwYXLDoW1ldHJvLCB5IHF1ZSBzaW1wbGlmaXF1ZSBsYSBlc2NyaXR1cmEgZGVsIG1hcC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTGVlciBsYSBiYXNlIGRlIEVQSCB0b21hbmRvIGNvbW8gYXJndW1lbnRvIGVsIGZpbGVfcGF0aApsZWVyX2Jhc2VfZXBoIDwtIGZ1bmN0aW9uKHBhdGgpIHsKICAjIExlY3R1cmEgZGUgYXJjaGl2bwogIHJlYWQudGFibGUocGF0aCxzZXA9IjsiLCBkZWM9IiwiLCBoZWFkZXIgPSBUUlVFLCBmaWxsID0gVFJVRSkgJT4lCiAgICAjIFNlbGVjY2lvbiBkZSB2YXJpYWJsZXMgcmVsZXZhbnRlcwogICAgc2VsZWN0KEFOTzQsVFJJTUVTVFJFLFJFR0lPTixQMjEsQ0gwNCwgQ0gwNikKfQoKIyBMZWVyIGEgdW4gZGF0YWZyYW1lIGxhcyB0YWJsYXMgZXNwZWNpZmljYWRhcyBlbiBlbCB2ZWN0b3IgZGUgYmFzZXMgaW5kaXZpZHVhbGVzCmJhc2VzX2RmIDwtIHRpYmJsZShiYXNlc19pbmRpdmlkdWFsZXNfcGF0aCkgJT4lCiAgbXV0YXRlKGJhc2UgPSBtYXAoLnggPSBiYXNlc19pbmRpdmlkdWFsZXNfcGF0aCwgLmYgPSBsZWVyX2Jhc2VfZXBoKSkKCmBgYAoKYGBge3J9CmJhc2VzX2RmCmBgYAoKRWwgcmVzdWx0YWRvIGVzIHVuIGRhdGFmcmFtZSBkb25kZSBsYSBjb2x1bW5hIF9fYmFzZV9fIHRpZW5lIGVuIGNhZGEgZmlsYSBvdHJvIGRhdGFmcmFtZSAgY29uIGxhIGJhc2UgZGUgbGEgRVBIIGRlIGVzZSBwZXLDrW9kby4gRXN0byBlcyBsbyBxdWUgbGxhbWFtb3MgdW4gX25lc3RlZCBERl8gbyBkYXRhZnJhbWUgYW5pZGFkby4KClNpIHF1ZXJlbW9zIGFicmlyICgiZGVzYW5pZGFyIikgZXN0YXMgZGF0YWZyYW1lcyBhbmlkYWRvcyB1c2Ftb3MgZWwgY29tYW5kbyBgdW5uZXN0KClgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIERlc2FuaWRhbW9zIGVsIGRhdGFmcmFtZQpiYXNlc19kZiA8LSBiYXNlc19kZiAlPiUgdW5uZXN0KCkKYmFzZXNfZGYKYGBgCgo+IMK/UXXDqSBwYXNhIHNpIGxvcyBERiBxdWUgdGVuZW1vcyBuZXN0ZWFkb3Mgbm8gdGllbmVuIGxhIG1pc21hIGNhbnRpZGFkIGRlIGNvbHVtbmFzPwoKRXN0byBtaXNtbyBsbyBwb2RlbW9zIHVzYXIgcGFyYSBmcmFnbWVudGFyIGVsIGRhdGFzZXQgcG9yIGFsZ3VuYSB2YXJpYWJsZSwgY29uIGxhIGZ1bmNpb24gYGdyb3VwX2J5KClgCgoKYGBge3J9CmJhc2VzX2RmICU+JSAKICAjIEFncnVwYXIgcG9yIHJlZ2lvbgogIGdyb3VwX2J5KFJFR0lPTikgJT4lIAogICMgQW5pZGFyCiAgbmVzdCgpCmBgYAoKQXPDrSwgcGFyYSBjYWRhIHJlZ2nDs24gdGVuZW1vcyB1biBERi4KCj4gwr8gRGUgcXXDqSBzaXJ2ZSB0b2RvIGVzdG8/CgpObyB0b2RvIGVuIGxhIHZpZGEgZXMgdW4gRGF0YWZyYW1lLiBIYXkgZXN0dWN0dXJhcyBkZSBkYXRvcyBxdWUgbm8gc2UgcHVlZGVuIG5vcm1hbGl6YXIgYSBmaWxhcyB5IGNvbHVtbmFzLiBFbiBlc29zIGNhc29zIHJlY3VycsOtYW1vcyB0cmFkaWNpb25hbG1lbnRlIGEgbG9zIGxvb3BzLiBDb24gTUFQIHBvZGVtb3MgdGVuZXIgbG9zIGVsZW1lbnRvcyBhZ3J1cGFkb3MgZW4gdW4gc8OzbG8gb2JqZXRvIHkgYcO6biBjb25zZXJ2YXIgc3VzIGZvcm1hcyBkaWZlcmVudGVzLgoKIyMgRWplbXBsby4gUmVncmVzacOzbiBsaW5lYWwKClNpIGJpZW4gbm8gbm9zIHZhbW9zIGEgbWV0ZXIgZW4gZWwgZGV0YWxsZSBkZWwgbW9kZWxvIGxpbmVhbCBlbiBlc3RlIGVuY3VlbnRybywgZXMgw7p0aWwgdXNhcmxvIGNvbW8gZWplbXBsbyBkZSBsbyBxdWUgcG9kZW1vcyBoYWNlciBjb24gTUFQLgoKUGxhbnRlYW1vcyBlbCBtb2RlbG8gCiQkClAyMSA9IFxiZXRhXzAgKyBcYmV0YV8xKkNIMDQgKyBcYmV0YV8yKkNIMDYKJCQKCkVzIHVuIG1vZGVsbyBxdWUgZXhwbGljYSBlbCBpbmdyZXNvIChQMjEpIGVuIGZ1bmNpb24gZGVsIHNleG8gKENIMDQpIHkgZWRhZCAoQ0gwNikKCmBgYHtyfQojIENhbGN1bGFtb3MgdW5hIHJlZ3Jlc2lvbiBsaW5lYWwgc29icmUgZWwgZGF0YXNldCBxdWUgdGllbmUgdG9kYXMgbGFzIG9ic2VydmFjaW9uZXMgZGUgRVBICmxtZml0IDwtIGxtKFAyMX5mYWN0b3IoQ0gwNCkrQ0gwNixkYXRhID0gYmFzZXNfZGYpCgojIFJlc3VtZW4gZGVsIG1vZGVsbwpzdW1tYXJ5KGxtZml0KQpgYGAKCihhbCBmaW5hbCBkZSBsYSBjbGFzZSBwb2RlbW9zIGNoYXJsYXIgc29icmUgbG9zIHJlc3VsdGFkb3MpCgpEZSBmb3JtYSBUaWR5LCBsYSBsaWJyZXLDrWEgYGJyb29tYCBub3MgZGEgbG9zIHJlc3VsdGFkb3Mgc29icmUgbG9zIGNvZWZpY2llbnRlcyBlbiB1biBERi4KCmBgYHtyfQpicm9vbTo6dGlkeShsbWZpdCkKYGBgCiAKIFNpIGxvIHF1ZXJlbW9zIGhhY2VyIHBvciByZWdpb24KIAojIyMgTG9vcGVhbmRvCgpgYGB7cn0KCnJlc3VsdGFkb3MgPC0gdGliYmxlKCkKCmZvciAocmVnaW9uIGluIHVuaXF1ZShiYXNlc19kZiRSRUdJT04pKSB7CiAgIyBDcmVhbW9zIHVuIGRhdGFzZXQgcG9yIHJlZ2lvbgogIGRhdGEgPC0gYmFzZXNfZGYgJT4lIAogICAgZmlsdGVyKFJFR0lPTj09cmVnaW9uKQogICMgQ2FsY3VsYXIgZWwgbW9kZWxvIGxpbmVhbAogIGxtZml0IDwtIGxtKFAyMX5mYWN0b3IoQ0gwNCkrQ0gwNixkYXRhID0gZGF0YSkKICAjIEd1YXJkYXIgbG9zIHJlc3VsdGFkb3MgZGUgbG9zIGNvZWZpY2llbnRlcyAKICBsbXRpZHkgPC0gYnJvb206OnRpZHkobG1maXQpCiAgIyBBc2lnbmFtb3MgbGEgdmFyaWFibGUgcmVnaW9uCiAgbG10aWR5JHJlZ2lvbiA8LSByZWdpb24KICAjIFVuaW1vcyBsb3MgZGF0YWZyYW1lcwogIHJlc3VsdGFkb3MgPC0gYmluZF9yb3dzKHJlc3VsdGFkb3MsbG10aWR5KQoKfQoKcmVzdWx0YWRvcwpgYGAKCiMjIFVzYW5kbyBNQVAKClByaW1lcm8gbWUgYXJtbyB1bmEgZnVuY2lvbiBxdWUgbWUgc2ltcGxpZmljYSBlbCBjb2RpZ28uIExhIGZ1bmNpb24gbm9zIGRldnVlbHZlIGxhIGluZm9ybWFjaW9uIGRlIGxvcyBjb2VmaWNpZW50ZXMgZGVsIG1vZGVsbyBsaW5lYWwgY2FsY3VsYWRvIHNvYnJlIGVsIGRhdGFmcmFtZSBfX3BvcmNpb25fXwoKYGBge3J9CmZ1bjwtZnVuY3Rpb24ocG9yY2lvbixncnVwbykgeyAgYnJvb206OnRpZHkobG0oUDIxfmZhY3RvcihDSDA0KStDSDA2LGRhdGEgPSBwb3JjaW9uKSl9CmBgYAoKYGBge3J9CmJhc2VzX2RmX2xtIDwtIGJhc2VzX2RmICU+JSAKICAjIEFncnVwYW1vcyBwb3IgcmVnaW9uCiAgZ3JvdXBfYnkoUkVHSU9OKSAlPiUKICAjIEFuaWRhbW9zCiAgbmVzdCgpICU+JSAKICAjIENyZWFtb3MgdW5hIGNvbHVtbmEgcXVlIHRlbmdhIGVsIGRhdGFmcmFtZSBjb21vIHJlc3VsdGFkbyBkZSBsYSBmdW5jaW9uCiAgbXV0YXRlKGxtID0gbWFwKGRhdGEsZnVuKSkKCmJhc2VzX2RmX2xtCgojIERlc2FuaWRhbW9zIHBvciBsYSB2YXJpYWJsZSBsbQpiYXNlc19kZl9sbSAlPiUgCiAgdW5uZXN0KGxtKQpgYGAKCk8gaW5jbHVzbyBtw6FzIGZhY2lsLCB1dGlsaXphbmRvIGBncm91cF9tb2RpZnlgIChxdWUgZXMgdW4gYXRham8gcXVlIHNvbG8gYWNlcHRhIERGKQoKYGBge3J9CmJhc2VzX2RmICU+JSAKICBncm91cF9ieShSRUdJT04pICU+JSAKICBncm91cF9tb2RpZnkoZnVuKQpgYGAKClBlcm8gTUFQIHNpcnZlIHBhcmEgb3BlcmFyIGNvbiBjdWFscXVpZXIgb2JqZXRvIGRlIFIuCgpQb3IgZWplbXBsbyBwb2RlbW9zIGd1YXJkYXIgZWwgX19vYmpldG9fXyBgUzM6bG1gIHF1ZSBlcyBsYSByZWdyZXNpb24gbGluZWFsIGVudHJlbmFkYS4gRXNlIG9iamV0byBubyBlcyBuaSB1biB2ZWN0b3IsIG5pIHVuYSBsaXN0YSwgbmkgdW4gREYuIE5vIGVzIHVuYSBlc3RydWN0dXJhIGRlIGRhdG9zLCBzaW5vIHF1ZSBlcyBhbGdvIGRpc3RpbnRvLCBjb24gX3Byb3BpZWRhZGVzXyBjb21vIGBwcmVkaWN0KClgIHBhcmEgcHJlZGVjaXIsIGVsIGBzdW1tYXJ5KClgIHF1ZSB2aW1vcywgZXRjLgoKYGBge3J9CiMgTGEgZnVuY2lvbiBjYWxjdWxhIGVsIG1vZGVsbyBsaW5lYWwgc29icmUgY2FkYSBwb3JjaW9uIGRlIGRhdG9zCmZ1bjwtZnVuY3Rpb24ocG9yY2lvbixncnVwbykgeyAgbG0oUDIxfmZhY3RvcihDSDA0KStDSDA2LGRhdGEgPSBwb3JjaW9uKX0KCmJhc2VzX2RmICU+JQogICMgQWdydXBhciBwb3IgcmVnaW9uCiAgZ3JvdXBfYnkoUkVHSU9OKSAlPiUKICAjIEFuaWRhcgogIG5lc3QoKSAlPiUKICAjIENhbGN1bGFyIGVsIG1vZGVsbyBsaW5lYWwKICBtdXRhdGUobG0gPSBtYXAoZGF0YSxmdW4pKQpgYGAKCgojIyBXYWxrCgpMYXMgZnVuY2lvbmVzIGBXYWxrYCBUaWVuZW4gbGEgbWlzbWEgZm9ybWEgcXVlIGxvcyBgbWFwYCwgcGVybyBzZSB1c2FuIGN1YW5kbyBsbyBxdWUgcXVlcmVtb3MgaXRlcmFyIG5vIGdlbmVyYSB1bmEgc2FsaWRhLCBzaW5vIHF1ZSBub3MgaW50ZXJlc2FuIGxvcyBlZmVjdG9zIHNlY3VuZGFyaW9zIHF1ZSBnZW5lcmFuLgoKYGBge3J9Cm1hcDIoLnggPSBBQkNfMTIzJExldHJhcywueSA9IEFCQ18xMjMkTnVtLC5mID0gZnVuY2lvbl9wcnVlYmEpWzE6M10KYGBgCgpgYGB7cn0Kd2FsazIoLnggPSBBQkNfMTIzJExldHJhcywueSA9IEFCQ18xMjMkTnVtLC5mID0gZnVuY2lvbl9wcnVlYmEpCmBgYAoKTm90ZW1vcyBxdWUgYHdhbGsyYCBubyBkZXZvbHZpw7MgdW4gcmVzdWx0YWRvCgpgYGB7cn0KIyBmdW5jaW9uIHBhcmEgaW1wcmltaXIKaW1wcmltaXJfc2FsaWRhIDwtIGZ1bmN0aW9uKHgseSl7CiAgcHJpbnQoZnVuY2lvbl9wcnVlYmEoeCx5KSkKfQoKIyBNYXAKbWFwMihBQkNfMTIzJExldHJhcyxBQkNfMTIzJE51bSxpbXByaW1pcl9zYWxpZGEpCmBgYAoKYGBge3J9CiMgV2Fsawp3YWxrMihBQkNfMTIzJExldHJhcyxBQkNfMTIzJE51bSxpbXByaW1pcl9zYWxpZGEpCgpgYGAKCkVzbyBxdWUgdmVtb3MgZXMgZWwgZWZlY3RvIHNlY3VuZGFyaW8gZGVudHJvIGRlIGxhIGZ1bmNpw7NuIChpbXByaW1pcikKCiMjIERpc2N1c2nDs24uCgo+IMK/Q3XDoW5kbyB1c2FyIGVzdGFzIGhlcnJhbWllbnRhcz8KCkEgbG8gbGFyZ28gZGVsIGN1cnNvIHZpbW9zIGRpZmVyZW50ZXMgdMOpY25pY2FzIHBhcmEgbWFuaXB1bGFjacOzbiBkZSBkYXRvcy4gRW4gcGFydGljdWxhciwgbGEgbGlicmVyw61hIGRwbHlyIG5vcyBwZXJtaXTDrWEgZsOhY2lsbWVudGUgbW9kaWZpY2FyIHkgY3JlYXIgbnVldmFzIHZhcmlhYmxlcywgYWdydXBhbmRvLiDCv0N1YW5kbyB1c2Ftb3MgYGRwbHlyYCB5IGN1YW5kbyB1c2Ftb3MgYHB1cnJyYD8KCi0gU2kgdHJhYmFqYW1vcyBzb2JyZSB1biBERiBzaW1wbGUsIHNpbiB2YXJpYWJsZXMgYW5pZGFkYXMgKGxvIHF1ZSBjb25vY8OtYW1vcyBoYXN0YSBob3kpIHBvZGVtb3MgdXNhciBgZHBseXJgCi0gU2kgcXVlcmVtb3MgdHJhYmFqYXIgY29uIERGIGFuaWRhZG9zLCBjb24gY29zYXMgcXVlIG5vIHNvbiBERiwgbyBzaSBlbCByZXN1bHRhZG8gZGUgbGEgb3BlcmFjacOzbiBxdWUgdmFtb3MgYSByZWFsaXphciBhIG5pdmVsIGFyY2hpdm8gZXMgYWxnbyBkaXN0aW50byBhIHVuIHZhbG9yIMO6bmljbywgbm9zIGNvbnZpZW5lIHVzYXIgYG1hcGAgeSBgcHVycnJgCgotIExhcyBmdW5jaW9uZXMgYHdhbGtgIHNvbiDDunRpbGVzIHBvciBlamVtcGxvIHBhcmEgZXNjcmliaXIgYXJjaGl2b3MgZW4gZGlzY28gZGUgZm9ybWEgaXRlcmF0aXZhLiBBbGdvIHF1ZSBubyBnZW5lcmEgdW5hIHNhbGlkYQoKIyBFamVyY2ljaW9zIHBhcmEgcHJhY3RpY2FyCi0gQ3JlYXIgdW5hICoqZnVuY2nDs24qKiBsbGFtYWRhIF9Ib2xhTXVuZG9fIHF1ZSBpbXByaW1hIGVsIHRleHRvICJIb2xhIG11bmRvIgotIENyZWFyIHVuYSAqKmZ1bmNpw7NuKiogcXVlIGRldnVlbHZhIGxhIHN1bWF0b3JpYSBkZSBsb3MgbsO6bWVyb3MgZW50ZXJvcyBjb21wcmVuZGlkb3MgZW50cmUgMSB5IHVuIHBhcsOhbWV0cm8gX3hfIGEgZGVmaW5pci4KCi0gQ3JlYXIgdW5hICoqZnVuY2nDs24qKiBxdWUgY2FsY3VsZSBsYSBmcmVjdWVuY2lhIGV4cGFuZGlkYSBwb3IgdW4gUE9OREVSQQotIFV0aWxpemFyIGRpY2hhIGZ1bmNpw7NuIHBhcmEgY2FsY3VsYXIgbGEgZnJlY3VlbmNpYSBwb2JsYWNpw7NuYWwgcG9yIFNleG8geSBSZWdpw7NuCgotIE1vZGlmaWNhciBsYSBmdW5jacOzbiBhbnRlcmlvciBwYXJhIHF1ZSBkZXZ1ZWx2YSB1biB2ZWN0b3IgY29uIGxhIGZyZWN1ZW5jaWEgbXVlc3RyYSAqKnkqKiBsYSBmcmVjdWVuY2lhIHBvYmxhY2lvbmFsCi0gVXRpbGl6YXIgbGEgZnVuY2nDs24gbW9kaWZpY2FkYSBwYXJhIGNhbGN1bGFyIGxhIGZyZWN1ZW5jaWFzIGZyZWN1ZW5jaWFzIG11ZXN0cmFsZXMgeSBwb2JsYWNpb25hbGVzIHBvciBTZXhvIHkgUmVnacOzbgo=